package io.gitee.guoguoleo.wrapper;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.util.NumberUtil;
import io.gitee.guoguoleo.enums.SqlLike;
import io.gitee.guoguoleo.model.WhereFieldInfo;
import io.gitee.guoguoleo.utils.FieldUtil;
import io.gitee.guoguoleo.utils.SqlUtil;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.update.UpdateSet;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

public class Wrapper<T> implements IWrapper<T> {

    protected Boolean andFlag;
    protected Boolean orFlag;
    protected List<WhereFieldInfo> whereExpressionList;
    //order by
    protected List<OrderByElement> orderByElementList;

    //set
    protected List<UpdateSet> updateSetList;

    protected List<String> selectColumnList;//要查询的列
    protected List<String> excludeSelectColumnList;//除外的列


    /**
     * and操作，不声明默认先使用
     *
     * @return this
     */
    public Wrapper<T> and() {
        this.andFlag = true;
        this.orFlag = false;
        return this;
    }

    /**
     * or操作，需要声明后使用
     *
     * @return this
     */
    public Wrapper<T> or() {
        this.andFlag = false;
        this.orFlag = true;
        return this;
    }


    /**
     * eq
     *
     * @param condition 前置条件
     * @param fieldName 字段名称
     * @param value     值
     * @return this
     */
    public Wrapper<T> eq(boolean condition, String fieldName, Object value) {
        if (condition) {
            Column column = new Column(FieldUtil.toField(fieldName));
            EqualsTo equalsTo;
            if (NumberUtil.isNumber(value.toString())) {
                equalsTo = new EqualsTo(column, new LongValue(value.toString()));
            } else {
                equalsTo = new EqualsTo(column, new StringValue(value.toString()));
            }
            this.setWhere(equalsTo);
        }
        return this;
    }


    /**
     * eq
     *
     * @param fieldName 字段名称
     * @param value     值
     * @return this
     */
    public Wrapper<T> eq(String fieldName, Object value) {
        return this.eq(true, fieldName, value);
    }

    /**
     * eq
     *
     * @param condition 前置条件
     * @param func1     字段名称
     * @param value     值
     * @return this
     */
    public Wrapper<T> eq(boolean condition, Func1<T, ?> func1, Object value) {
        String fieldName = LambdaUtil.getFieldName(func1);
        return this.eq(condition, fieldName, value);
    }

    /**
     * eq
     *
     * @param func1 字段名称
     * @param value 值
     * @return this
     */
    public Wrapper<T> eq(Func1<T, ?> func1, Object value) {
        String fieldName = LambdaUtil.getFieldName(func1);
        return this.eq(true, fieldName, value);
    }

    /**
     * in
     *
     * @param condition 前置条件
     * @param fieldName 名称
     * @param valueList 值列表
     * @return this
     */
    public Wrapper<T> in(boolean condition, String fieldName, Collection<? extends Serializable> valueList) {
        if (condition && CollUtil.isNotEmpty(valueList)) {
            Column column = new Column(FieldUtil.toField(fieldName));
            InExpression inExpression = new InExpression();
            inExpression.setLeftExpression(column);

            //region 将list加括号
            ParenthesedExpressionList<Expression> values = new ParenthesedExpressionList<>();
            for (Serializable value : valueList) {
                if (value instanceof Integer || value instanceof Long) {
                    values.addExpression(new LongValue(value.toString()));
                } else {
                    values.addExpression(new StringValue(value.toString()));
                }
            }
            //endregion

            inExpression.withRightExpression(values);

            this.setWhere(inExpression);
        }
        return this;
    }

    /**
     * in
     *
     * @param fieldName 名称
     * @return this
     */
    public Wrapper<T> in(String fieldName, Collection<? extends Serializable> valueList) {
        return this.in(true, fieldName, valueList);
    }

    /**
     * in
     *
     * @param condition 前置条件
     * @param fieldName 名称
     * @param values    值列表
     * @return this
     */
    public Wrapper<T> in(boolean condition, String fieldName, Serializable... values) {
        List<Serializable> valueList = ListUtil.toList(values);
        return this.in(condition, fieldName, valueList);
    }

    /**
     * in
     *
     * @param fieldName 名称，支持驼峰或者下划线
     * @return this
     */
    public Wrapper<T> in(String fieldName, Serializable... values) {
        return this.in(true, fieldName, values);
    }


    /**
     * in
     *
     * @param condition 前置条件
     * @param func1     字段
     * @param valueList 值列表
     * @return this
     */
    public Wrapper<T> in(boolean condition, Func1<T, ?> func1, Collection<? extends Serializable> valueList) {
        String fieldName = LambdaUtil.getFieldName(func1);
        return this.in(condition, fieldName, valueList);
    }

    /**
     * in
     *
     * @param func1     字段
     * @param valueList 值列表
     * @return this
     */
    public Wrapper<T> in(Func1<T, ?> func1, Collection<? extends Serializable> valueList) {
        String fieldName = LambdaUtil.getFieldName(func1);
        return this.in(true, fieldName, valueList);
    }

    /**
     * in
     *
     * @param condition 前置条件
     * @param func1     字段
     * @param values    值列表
     * @return this
     */
    public Wrapper<T> in(boolean condition, Func1<T, ?> func1, Serializable... values) {
        String fieldName = LambdaUtil.getFieldName(func1);
        List<Serializable> valueList = ListUtil.toList(values);
        return this.in(condition, fieldName, valueList);
    }

    /**
     * in
     *
     * @param func1  字段
     * @param values 值列表
     * @return this
     */
    public Wrapper<T> in(Func1<T, ?> func1, Serializable... values) {
        String fieldName = LambdaUtil.getFieldName(func1);
        List<Serializable> valueList = ListUtil.toList(values);
        return this.in(true, fieldName, valueList);
    }


    /**
     * like
     *
     * @param condition 前置条件
     * @param fieldName 字段名称
     * @param value     值
     * @param sqlLike   like类型
     * @return this
     */
    public Wrapper<T> like(boolean condition, String fieldName, Object value, SqlLike sqlLike) {
        if (condition) {
            Column column = new Column(FieldUtil.toField(fieldName));
            LikeExpression likeExpression = new LikeExpression();
            likeExpression.withLeftExpression(column);
            likeExpression.withRightExpression(SqlUtil.concatLike(value, sqlLike));

            this.setWhere(likeExpression);
        }
        return this;
    }

    /**
     * like
     *
     * @param condition 前置条件
     * @param fieldName 字段名称
     * @param value     值
     * @return this
     */
    public Wrapper<T> like(boolean condition, String fieldName, Object value) {
        return this.like(condition, fieldName, value, SqlLike.DEFAULT);
    }

    /**
     * like
     *
     * @param fieldName 字段名称
     * @param value     值
     * @return this
     */
    public Wrapper<T> like(String fieldName, Object value) {
        return this.like(true, fieldName, value, SqlLike.DEFAULT);
    }


    /**
     * like
     *
     * @param condition 前置条件
     * @param func1     字段名称
     * @param value     值
     * @param sqlLike   like类型
     * @return this
     */
    public Wrapper<T> like(boolean condition, Func1<T, ?> func1, Object value, SqlLike sqlLike) {
        String fieldName = LambdaUtil.getFieldName(func1);
        return this.like(condition, fieldName, value, sqlLike);
    }

    /**
     * like
     *
     * @param condition 前置条件
     * @param func1     字段名称
     * @param value     值
     * @return this
     */
    public Wrapper<T> like(boolean condition, Func1<T, ?> func1, Object value) {
        String fieldName = LambdaUtil.getFieldName(func1);
        return this.like(condition, fieldName, value, SqlLike.DEFAULT);
    }

    /**
     * like
     *
     * @param func1 字段名称
     * @param value 值
     * @return this
     */
    public Wrapper<T> like(Func1<T, ?> func1, Object value) {
        String fieldName = LambdaUtil.getFieldName(func1);
        return this.like(true, fieldName, value, SqlLike.DEFAULT);
    }

    /**
     * 设置普通搜索条件
     *
     * @param expression 表达式对象
     */
    private void setWhere(Expression expression) {
        if (this.andFlag) {
            WhereFieldInfo whereFieldInfo = new WhereFieldInfo()
                    .setAndFlag(Boolean.TRUE)
                    .setOrFlag(Boolean.FALSE)
                    .setExpression(expression);
            this.whereExpressionList.add(whereFieldInfo);
        }

        if (this.orFlag) {
            WhereFieldInfo whereFieldInfo = new WhereFieldInfo()
                    .setAndFlag(Boolean.FALSE)
                    .setOrFlag(Boolean.TRUE)
                    .setExpression(expression);
            this.whereExpressionList.add(whereFieldInfo);
        }
    }

    /**
     * asc排序
     *
     * @param condition     前置条件
     * @param fieldNameList 字段列表
     * @return this
     */
    public Wrapper<T> orderByAsc(boolean condition, Collection<String> fieldNameList) {
        if (condition && CollUtil.isNotEmpty(fieldNameList)) {
            for (String fieldName : fieldNameList) {
                OrderByElement orderByElement = new OrderByElement();
                orderByElement.setAsc(true);
                Column column = new Column(FieldUtil.toField(fieldName));
                orderByElement.setExpression(column);
                this.orderByElementList.add(orderByElement);
            }
        }
        return this;
    }

    /**
     * asc排序
     *
     * @param fieldNameList 字段列表
     * @return this
     */
    public Wrapper<T> orderByAsc(Collection<String> fieldNameList) {
        return this.orderByAsc(true, fieldNameList);
    }

    /**
     * asc排序
     *
     * @param condition  前置条件
     * @param fieldNames 字段列表
     * @return this
     */
    public Wrapper<T> orderByAsc(boolean condition, String... fieldNames) {
        List<String> fieldNameList = ListUtil.toList(fieldNames);
        return this.orderByAsc(condition, fieldNameList);
    }

    /**
     * asc排序
     *
     * @param fieldNames 字段列表
     * @return this
     */
    public Wrapper<T> orderByAsc(String... fieldNames) {
        List<String> fieldNameList = ListUtil.toList(fieldNames);
        return this.orderByAsc(true, fieldNameList);
    }

    /**
     * asc排序
     *
     * @param condition 前置条件
     * @param func1s    字段列表
     * @return this
     */
    @SafeVarargs
    public final Wrapper<T> orderByAsc(boolean condition, Func1<T, ?>... func1s) {
        List<String> fieldNameList = ListUtil.toList(func1s).stream()
                .map(LambdaUtil::getFieldName)
                .toList();
        return this.orderByAsc(condition, fieldNameList);
    }

    /**
     * asc排序
     *
     * @param func1s 字段列表
     * @return this
     */
    @SafeVarargs
    public final Wrapper<T> orderByAsc(Func1<T, ?>... func1s) {
        return this.orderByAsc(true, func1s);
    }


    /**
     * desc排序
     *
     * @param condition     前置条件
     * @param fieldNameList 字段列表
     * @return this
     */
    public Wrapper<T> orderByDesc(boolean condition, Collection<String> fieldNameList) {
        if (condition && CollUtil.isNotEmpty(fieldNameList)) {
            for (String fieldName : fieldNameList) {
                OrderByElement orderByElement = new OrderByElement();
                orderByElement.setAsc(false);
                Column column = new Column(FieldUtil.toField(fieldName));
                orderByElement.setExpression(column);
                this.orderByElementList.add(orderByElement);
            }
        }
        return this;
    }

    /**
     * desc排序
     *
     * @param fieldNameList 字段列表
     * @return this
     */
    public Wrapper<T> orderByDesc(Collection<String> fieldNameList) {
        return this.orderByDesc(true, fieldNameList);
    }

    /**
     * desc排序
     *
     * @param condition  前置条件
     * @param fieldNames 字段列表
     * @return this
     */
    public Wrapper<T> orderByDesc(boolean condition, String... fieldNames) {
        List<String> fieldNameList = ListUtil.toList(fieldNames);
        return this.orderByDesc(condition, fieldNameList);
    }

    /**
     * desc排序
     *
     * @param fieldNames 字段列表
     * @return this
     */
    public Wrapper<T> orderByDesc(String... fieldNames) {
        List<String> fieldNameList = ListUtil.toList(fieldNames);
        return this.orderByDesc(true, fieldNameList);
    }

    /**
     * desc排序
     *
     * @param condition 前置条件
     * @param func1s    字段列表
     * @return this
     */
    @SafeVarargs
    public final Wrapper<T> orderByDesc(boolean condition, Func1<T, ?>... func1s) {
        List<String> fieldNameList = ListUtil.toList(func1s).stream()
                .map(LambdaUtil::getFieldName)
                .toList();
        return this.orderByDesc(condition, fieldNameList);
    }

    /**
     * desc排序
     *
     * @param func1s 字段列表
     * @return this
     */
    @SafeVarargs
    public final Wrapper<T> orderByDesc(Func1<T, ?>... func1s) {
        return this.orderByDesc(true, func1s);
    }


    /**
     * 更新用set字段与值
     *
     * @param condition 前置条件
     * @param fieldName 字段名
     * @param value     值
     * @return this
     */
    public Wrapper<T> set(boolean condition, String fieldName, Object value) {
        if (this.updateSetList == null) {
            throw new RuntimeException("该操作不支持set");
        }
        if (condition) {
            UpdateSet updateSet = new UpdateSet();
            Column column = new Column(FieldUtil.toField(fieldName));
            if (value == null){
                updateSet.add(column, new NullValue());
            }else {
                if (NumberUtil.isNumber(value.toString())) {
                    LongValue longValue = new LongValue(value.toString());
                    updateSet.add(column, longValue);
                } else {
                    StringValue stringValue = new StringValue(value.toString());
                    updateSet.add(column, stringValue);
                }
            }
            this.updateSetList.add(updateSet);
        }
        return this;
    }

    /**
     * 更新用set字段与值
     *
     * @param fieldName 字段名
     * @param value     值
     * @return this
     */
    public Wrapper<T> set(String fieldName, Object value) {
        return this.set(true, fieldName, value);
    }

    /**
     * 更新用set字段与值
     *
     * @param condition 前置条件
     * @param func1     字段名
     * @param value     值
     * @return this
     */
    public Wrapper<T> set(boolean condition, Func1<T, ?> func1, Object value) {
        String fileName = LambdaUtil.getFieldName(func1);
        return this.set(condition, fileName, value);
    }

    /**
     * 更新用set字段与值
     *
     * @param func1 字段名
     * @param value 值
     * @return this
     */
    public Wrapper<T> set(Func1<T, ?> func1, Object value) {
        String fileName = LambdaUtil.getFieldName(func1);
        return this.set(true, fileName, value);
    }

    /**
     * 当前字段查询
     */
    public Wrapper<T> select(List<String> fieldNameList){
        if (CollUtil.isEmpty(fieldNameList)) {
            throw new RuntimeException("至少要有一个查询字段");
        }
        for (String fieldName : fieldNameList) {
            this.selectColumnList.add(FieldUtil.toField(fieldName));
        }
        return this;
    }

    /**
     * 当前字段查询
     */
    public Wrapper<T> select(String...fieldNames){
        List<String> fieldNameList = ListUtil.toList(fieldNames);
        return this.select(fieldNameList);
    }

    /**
     * 当前字段查询
     */
    @SafeVarargs
    public final Wrapper<T> select(Func1<T, ?>... func1s){
        List<String> func1List = ListUtil.toList(func1s).stream()
                .map(LambdaUtil::getFieldName)
                .toList();
        return this.select(func1List);
    }


    /**
     * 当前字段查询
     */
    public Wrapper<T> exclude(List<String> fieldNameList){
        for (String fieldName : fieldNameList) {
            this.excludeSelectColumnList.add(FieldUtil.toField(fieldName));
        }
        return this;
    }

    /**
     * 当前字段查询
     */
    public Wrapper<T> exclude(String...fieldNames){
        List<String> fieldNameList = ListUtil.toList(fieldNames);
        return this.exclude(fieldNameList);
    }

    /**
     * 当前字段查询
     */
    @SafeVarargs
    public final Wrapper<T> exclude(Func1<T, ?>... func1s){
        List<String> func1List = ListUtil.toList(func1s).stream()
                .map(LambdaUtil::getFieldName)
                .toList();
        return this.exclude(func1List);
    }

}
